package com.lagou.filter;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 业务描述：
 * Project Name: homework-02
 * Package Name: com.lagou.web.filter
 * Author: yangcun
 * Date 2020-08-30
 */
@Activate(group = {CommonConstants.PROVIDER})
public class TPMonitorFilter implements Filter {

    private static Map<String, Cache<String, Long>> map = new ConcurrentHashMap();

    private static final String[] methodNames = new String[]{"methodA", "methodB", "methodC"};

    private static Long timej = 0L;

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        String methodName = invocation.getMethodName();
        long startTime = System.currentTimeMillis();

        try {
            return invoker.invoke(invocation);
        } finally {
            long costTime = System.currentTimeMillis() - startTime;
            TPMonitorFilter.saveMethod(methodName, costTime);
            if (timej == 0) {
                timej = startTime + 5000;
            } else if (startTime > timej) {
                timej = startTime + 5000;
                System.out.println("-------------------tp计算信息----------------------");
                System.out.println("-------------------tp90----------------------");
                printTp(0.9);
                System.out.println("-------------------tp99----------------------");
                printTp(0.99);
            }
        }
    }

    private void printTp(double tpValue) {
        for (String methodName : methodNames) {
            System.out.println(methodName + " tp" + tpValue * 100 + ":" + getMethodTp(methodName, tpValue));
        }
    }

    public static void saveMethod(String methodName, long costTime) {
        Cache<String, Long> cache = getMethodCache(methodName);
        cache.put(UUID.randomUUID().toString(), costTime);
    }

    public static Cache<String, Long> getMethodCache(String methodName) {
        Cache<String, Long> cache = null;
        if (!map.containsKey(methodName)) {
            synchronized (TPMonitorFilter.class) {
                if (!map.containsKey(methodName)) {
                    map.putIfAbsent(methodName, getDefaultCache());
                }
            }
        }
        cache = map.get(methodName);
        return cache;
    }

    public static long getMethodTp(String method, double tpValue) {
        Cache<String, Long> cache = getMethodCache(method);
        ConcurrentMap<String, Long> concurrentMap = cache.asMap();

        ArrayList<Long> values = new ArrayList<>(concurrentMap.values());
        if (values.isEmpty()) {
            return 0;
        }
        List<Long> collect = values.stream().sorted().collect(Collectors.toList());
        int index = (int) (collect.size() * tpValue);
        return collect.get(index);
    }

    public static Cache getDefaultCache() {
        Cache<String, String> cache = CacheBuilder.newBuilder()
                .maximumSize(20000) // 设置缓存的最大容量
                .expireAfterWrite(1, TimeUnit.MINUTES) // 设置缓存在写入一分钟后失效
                .concurrencyLevel(20) // 设置并发级别为10
                .build();
        return cache;
    }

}
